home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Mac Magazin/MacEasy 32
/
Mac Magazin and MacEasy Magazine CD - Issue 32.iso
/
Grafik & Text
/
OzTeX3.0
/
BibTeX
/
bibEngine.tcl
next >
Wrap
Text File
|
1997-02-12
|
19KB
|
608 lines
## -*-Tcl-*-
# ###################################################################
# BibTeX for MacOS -- scripts for GURL interaction with Alpha.
#
# FILE: "bibEngine.tcl"
# created: 13/11/96 {1:05:50 am}
# last update: 12/2/97 {12:41:23 pm}
# Author: Vince Darley
# E-mail: <darley@fas.harvard.edu>
# mail: Division of Applied Sciences, Harvard University
# Oxford Street, Cambridge MA 02138, USA
# www: <http://www.fas.harvard.edu/~darley/>
#
# INSTALLATION PROCEDURE:
#
# Add the following line to your "prefs.tcl" (a file located in Alpha's
# preferences folder inside your Preferences folder in your System folder):
#
# eventHandler GURL GURL GURLHandler
#
# This declares an event handler so we receive GURL events from
# the BibTeX application (Note: do not copy the '#' at the beginning of
# the line). Now open Internet Config, select 'helpers' and 'add' a
# helper for 'bibresult', and select as helper the application 'Alpha'.
#
# Then move this file to the 'UserCode' folder inside the 'Tcl' folder
# which is located in the same place as the application Alpha. Now
# open Alpha, hit 'cmd-Y', and select 'Rebuild Tcl Indices' from the
# menu. After a minute or so, you can quit Alpha, and installation is
# complete. If the old file 'BibTeXAlphaScripts.tcl' exists, delete it.
#
# The only further choice you have is the value of the variable
# Bib_AutoIndex, which you can set below.
#
# (For the technically minded: I use Internet Config to do the dirty work
# of sending apple-events for me. One day I'll write my own code, but this
# hack works quite well for the moment.)
#
# modified by rev reason
# -------- --- --- -----------
# 13/11/96 VMD 1.0 original -- for use with BibTeX 1.1.4
# 22/11/96 VMD 1.1 various improvements plus a name change
# 31/1/97 VMD 1.2 handles some warnings and .bst files better now
# 6/2/97 VMD 1.3 added some features, handles some more obscure errors
# ###################################################################
##
# 0=never make index (except manually)
# 1=ask user when necessary
# 2=always remake when necessary
# NOTE: set this to _your_ preference out of the above.
set Bib_AutoIndex 1
# Used by bibPickBibliography to set a default in the listpick dialog
# It's useful because you will often want to add a bunch of new items
# in a row to the same bibliography.
# NOTE: this is set by my code, not you.
set Bib_defaultBib ""
##
# -------------------------------------------------------------------------
#
# "GURLHandler" --
#
# Handle general GURL events by extracting the type 'ftp', 'http',…
# and calling a procedure ${type}GURLHandler with a single parameter
# which is the extracted resource. Can be put to more general use.
# -------------------------------------------------------------------------
##
proc GURLHandler {msg} {
if ![regsub {.*“(.*)”.*} $msg {\1} gurl] {
alertnote "Didn't understand GURL: $msg"
return
}
set GURLtype [lindex [split $gurl ":"] 0]
set GURLvalue [string range $gurl [expr 1+[string length $GURLtype]] end]
if [catch {${GURLtype}GURLHandler $GURLvalue} msg] {
message $msg
}
}
##
# -------------------------------------------------------------------------
#
# "bibresultGURLHandler" --
#
# Handle 'bibresult' GURLs, as sent by the application BibTeX. These
# goto bibliography files, errors, warnings etc. We do the parsing here.
# See BibTeX's readme file for the syntax of the message.
# -------------------------------------------------------------------------
##
proc bibresultGURLHandler {msg} {
# Extract base .aux file name (full path description or 'Unknown')
set bpos [string first ".aux:" $msg]
set base_aux [string range $msg 0 [incr bpos 3]]
# Get rest of message
set msg [string range $msg [incr bpos 2] end]
# if it's a file name; we need to open it:
if [regsub {.*: ([^.]+.(aux|bst|bib))([ \t].*)?} $msg {\1} filename] {
set rest [string range $msg [expr [string first $filename $msg] + [string length $filename] ] end]
bib_OpenFile ${filename} [file dirname $base_aux]
if {[string trim $rest] == "not found"} {
alertnote "This file was not found by BibTeX. You should either move it to another location, or add to BibTeX's search paths."
}
return
}
switch [lindex [split $msg "-"] 0] {
"Warning" {
# extract warning type and find the entry
# the last item is the entry (minus quotes possibly)
set realmsg [set msg [string range $msg 9 end]]
if {[string first ";" $msg] != -1} {
# we have some stuff _after_ the item
set msg [lindex [split $msg ";"] 0]
}
# the msg ends in the bib entry
set llen [llength $msg]
set item [string trim [lindex $msg [incr llen -1]] {"}]
set warning [lrange $msg 0 [incr llen -1]]
if { $warning == "I didn't find a database entry for" } {
# no entry exists, prompt to make one
bib_NoEntryExists $item $base_aux
return
} else {
# go to a current entry
bibGotoEntry $item
beep
message "Warning--$realmsg"
return
}
}
default {
bib_GotoError $msg [file dirname $base_aux]
}
}
}
##
# -------------------------------------------------------------------------
#
# "bib_OpenFile" --
#
# Given a filename, and the directory of the base '.aux' file, try and
# find the file. If we don't succeed, pass the request onto the TeX
# code.
# -------------------------------------------------------------------------
##
proc bib_OpenFile {filename {dir ""}} {
# look where base file was
if {![catch {openFileQuietly "${dir}:${filename}"}]} {
return
}
# look in bibtex inputs folder
global bibtexSig
if {![catch {openFileQuietly "[file dirname [nameFromAppl $bibtexSig]]:BibTeX inputs:${filename}"}]} {
return
}
# look in all usual tex places
openTeXFile "$filename"
return
}
##
# -------------------------------------------------------------------------
#
# "bib_NoEntryExists" --
#
# No entry exists in the known .bib files. Either add an entry, possibly
# in a new bibliography file, or add a .bib file to those currently
# searched.
# -------------------------------------------------------------------------
##
proc bib_NoEntryExists {item {basefile ""}} {
set choice [prompt \
"No entry '$item' exists. What do you want to do?" \
"New entry" "Choices" \
"New entry" "New entry in new bibliography file" \
"Add .bib file to \\bibliography\{…\}" ]
switch $choice {
"New entry" {
# need to pick a .bib file
set bibfile [bibPickBibliography 1 \
"Select a bibliography file to which to add an entry"]
openTeXFile $bibfile
global entryNames
bibFormatSetup
newEntry [listpick -p "Which type of entry?" $entryNames]
insertText $item
nextTabStop
}
"New entry in new bibliography file" {
set bibfile [putfile "Save new bibliography as…" ".bib"]
if {$bibfile == ""} {
error "No bibliography file selected."
} else {
new -n $bibfile
}
global entryNames
bibFormatSetup
newEntry [listpick -p "Which type of entry?" $entryNames]
insertText $item
nextTabStop
}
"Add .bib file to \\bibliography\{…\}" {
if {$basefile == ""} {set basefile [TeX_currentBaseFile]}
# find .aux and open base .tex/.ltx
set base [lindex [split $basefile "."] 0]
if [file exists ${base}.tex] {
set base ${base}.tex
} elseif [file exists ${base}.ltx] {
set base ${base}.ltx
} else {
error "Base file with name '${base}.xxx' not found."
}
openFileQuietly ${base}
# find bibliography, position cursor and add
endOfBuffer
if [catch {set pos [search -f 0 -r 0 -m 0 "\\bibliography\{" [getPos]]}] {
# add the environment
set pos [search -f 0 "\\end\{document\}" [getPos]]
goto [lindex $pos 0]
set preinsert "\\bibliography\{"
set postinsert "\}\r\r"
} else {
set preinsert ""
set postinsert ","
goto [lindex $pos 1]
}
set bibfile [bibPickBibliography 0 \
"Select a bibliography file to add"]
insertText "${preinsert}[lindex [split $bibfile "."] 0]${postinsert}"
}
"Cancel" {
# nothing
}
}
}
##
# -------------------------------------------------------------------------
#
# "bib_GotoError" --
#
# Parse and goto a specific error in a particular file. Look locally for
# the correct text in case we've edited the file.
# -------------------------------------------------------------------------
##
proc bib_GotoError {msg {dir ""}} {
# is it an 'I found no xxxx while reading file yyy' error?
if [regsub {I found no .*---while reading file (.*)} $msg {\1} filename] {
bib_OpenFile $filename $dir
beep
message $msg
return
}
# It's a more specific error.
# Extract type, line, filename, and position of error
set errtype [lindex [split $msg "-"] 0]
if ![regsub {.*line ([0-9]+) .*} $msg {\1} line] {
error "Failed to parse line number from BibTeX error"
}
if ![regsub {.*of file (.*) a .*} $msg {\1} filename] {
error "Failed to parse filename from BibTeX error"
}
if ![regsub {.*a '(.*)' at.*} $msg {\1} problem] {
error "Failed to parse problem text from BibTeX error"
}
if ![regsub {.*at (.*)} $msg {\1} linepos] {
error "Failed to parse line position from BibTeX error"
}
bib_OpenFile $filename $dir
goto [rowColToPos $line $linepos]
# Un-map the encoding we did on the other end.
regsub "‘" $problem "\{" problem
regsub "’" $problem "\}" problem
# Un-map the encoding we did on the other end.
regsub "‘" $errtype "\{" errtype
regsub "’" $errtype "\}" errtype
set pos [getPos]
if {[getText [lineStart $pos] $pos] != $problem} {
# we've edited the file; look locally
set pr "^[quoteExpr2 $problem]"
if {![catch {search -f 0 -r 1 -l [expr $pos - 300] $pr $pos} found]} {
set pos [lindex $found 1]
} elseif {![catch {search -f 1 -r 1 -l [expr $pos + 300] $pr $pos} found]} {
set pos [lindex $found 1]
}
}
select [lineStart $pos] $pos
beep
message "$errtype"
return
}
##
# -------------------------------------------------------------------------
#
# "TeXEnsureSearchPathSet" --
#
# Make sure TeX mode has built our search path, so we can find
# bibliography files. Perhaps we should have our own variable
# for these?
# -------------------------------------------------------------------------
##
proc TeXEnsureSearchPathSet {} {
global TeXSearchPath
if { [llength $TeXSearchPath] == 0 } {
message "building TeX search path…"
set TeXSearchPath [buildTeXSearchPath]
message ""
}
}
##
# -------------------------------------------------------------------------
#
# "bibPickBibliography" --
#
# Put up a list-dialog so the user can select a bibliography file for
# some action (taken by the caller). Can also create a new file if
# desired.
# -------------------------------------------------------------------------
##
proc bibPickBibliography {{allowNew 1} {prompt "Pick a bibliography file"}} {
set biblist [bibListAllBibliographies]
if $allowNew {
lappend biblist {New file…}
}
global Bib_defaultBib
set bibfile [listpick -p $prompt -L $Bib_defaultBib $biblist]
if {$bibfile == ""} {
error "No bibliography file selected."
} elseif {$bibfile == "New file…" } {
set bibfile [putfile "Save new bibliography as…" ".bib"]
if {$bibfile == ""} {
error "No bibliography file selected."
} else {
set fout [open $bibfile w]
close $fout
}
}
return [file tail [set Bib_defaultBib $bibfile]]
}
##
# -------------------------------------------------------------------------
#
# "bibListAllBibliographies" --
#
# Return all bibliographies on the search path. Optionally only return
# those which are in a given .aux file.
# -------------------------------------------------------------------------
##
proc bibListAllBibliographies { {auxfile ""} } {
TeXEnsureSearchPathSet
global TeXSearchPath
set biblist {}
if {$auxfile == "" || [catch {set fid [open "$auxfile" r]}]} {
foreach d $TeXSearchPath {
eval lappend biblist [glob -nocomplain ${d}*.bib]
}
} else {
set bibs {}
# get list of bibs from .aux file
set cid [scancontext create]
scanmatch $cid {bibdata\{([^\}]*)\}} {
eval lappend bibs [split $matchInfo(submatch0) ","]
}
scanfile $cid $fid
close $fid
scancontext delete $cid
# find the full paths
foreach b $bibs {
foreach d $TeXSearchPath {
if [file exists ${d}${b}.bib] {
lappend biblist ${d}${b}.bib
break
}
}
}
}
return $biblist
}
##
# -------------------------------------------------------------------------
#
# "bibGotoEntry" --
#
# Look for a bib entry in the given list of files, or if that fails or
# isn't given, look in all available bib files on the search path.
# -------------------------------------------------------------------------
##
proc bibGotoEntry {entry {biblist {}}} {
if ![catch {bib_GotoEntryFromIndex $entry}] {
return
}
if {[llength $biblist] && ![catch {bib_GotoEntry $entry $biblist}]} {
return
}
if ![catch {bib_GotoEntry $entry [bibListAllBibliographies]}] {
return
}
beep
error "Can't find entry '$entry' in the .bib file(s)"
}
##
# -------------------------------------------------------------------------
#
# "bib_GotoEntryFromIndex" --
#
# Look in the bibIndex and find an entry very quickly.
# -------------------------------------------------------------------------
##
proc bib_GotoEntryFromIndex {entry} {
set bibTopPat {@([a-zA-Z]+)[\{\(][ ]*}
global PREFS
# if it fails, but we succeed later, we will have the opportunity
# to rebuild the bibIndex
if [file exists "${PREFS}:bibIndex"] {
source "${PREFS}:bibIndex"
global bibIndex
foreach f [array names bibIndex] {
if [regexp "\[ \r\n\]$entry\[ \r\n\]" "$bibIndex($f)"] {
openFileQuietly $f
set p [search -f 1 -r 1 $bibTopPat$entry 0]
eval select $p
centerRedraw
eval select $p
unset bibIndex
return
}
}
unset bibIndex
}
error "Entry '$entry' not found in bibIndex"
}
##
# -------------------------------------------------------------------------
#
# "bib_FindAllEntries" --
#
# Find all entries with a given prefix, optionally attaching the titles
# of the entries (this requires a bibDatabase file to be setup). Used
# by TeX citation completions: \cite{Darley<cmd-Tab>
# -------------------------------------------------------------------------
##
proc bib_FindAllEntries {eprefix {withtitles 1}} {
global PREFS
set matches {}
if $withtitles {
if ![file exists "${PREFS}:bibDatabase"] {
if {[askyesno "No bibDatabase exists, shall I make one?"]=="yes"} {
bibMakeDatabase
} else {
error "No bib database exists"
}
}
set cid [scancontext create]
scanmatch $cid "^${eprefix}" {
lappend matches [list $matchInfo(line)]
}
set fid [open "${PREFS}:bibDatabase" r]
scanfile $cid $fid
close $fid
scancontext delete $cid
} else {
global bibIndex
source "${PREFS}:bibIndex"
foreach f [array names bibIndex] {
if { [set matched [modeListCompletions $eprefix "bibIndex(${f})"]] != 0 } {
eval lappend matches $matched
}
}
unset bibIndex
}
return $matches
}
##
# -------------------------------------------------------------------------
#
# "bib_GotoEntry" --
#
# Find a bib entry in one of the given list of files, and signal an
# error if the entry isn't found. I think this is the quickest way.
# -------------------------------------------------------------------------
##
proc bib_GotoEntry {entry biblist} {
set bibTopPat {@([a-zA-Z]+)[\{\(][ ]*}
set cid [scancontext create]
scanmatch $cid $bibTopPat$entry {
set found "$matchInfo(offset)"
}
set found ""
foreach f $biblist {
message "Searching [file tail $f]…"
if {![catch {set fid [open $f]}]} {
scanfile $cid $fid
close $fid
if {$found != ""} {
openFileQuietly $f
goto $found
centerRedraw
select $found [nextLineStart $found]
scancontext delete $cid
global Bib_AutoIndex
# make the index since it was obviously out of date
if {$Bib_AutoIndex == 2 || [askyesno "The bibIndex is obviously out of date. Rebuild?"]=="yes"} {
bibMakeIndex
}
return
}
}
}
scancontext delete $cid
error "Entry '$entry' not found."
}
##
# -------------------------------------------------------------------------
#
# "bibMakeIndex" --
#
# Build the bibIndex file which allows for very fast lookup of bib
# entries.
# -------------------------------------------------------------------------
##
proc bibMakeIndex {} {
global PREFS
set bibTopPat2 {^[ ]*@([a-zA-Z]+)[\{\(][ ]*([^=, ]+)}
set cid [scancontext create]
# this will actually mark strings as well
scanmatch $cid $bibTopPat2 {
if {[string tolower $matchInfo(submatch0)] != "string"} {
lappend found $matchInfo(submatch1)
}
}
set bdatout [open "${PREFS}:bibDatabase" w]
set bout [open "${PREFS}:bibIndex" w]
puts $bout "# Bibliography index file for quick reference lookup"
puts $bout "# Created on [mtime [now]]"
puts $bdatout "# Bibliography database file for quick reference lookup"
puts $bdatout "# Created on [mtime [now]]"
foreach f [bibListAllBibliographies] {
set found {}
puts $bout "set \"bibIndex($f)\" \{"
message "Scanning [file tail $f]…"
if {![catch {set fid [open $f]}]} {
scanfile $cid $fid
close $fid
}
# we sort so we can search it efficiently for all entries with
# a given prefix.
puts $bout " [lsort $found] "
puts $bout "\}"
}
close $bout
scancontext delete $cid
message "bibIndex creation complete"
}
##
# -------------------------------------------------------------------------
#
# "bibMakeDatabase" --
#
# Build the bibDatabase which allows speedy completion of citations and
# contains titles, so that you can pick the correct completion easily.
# -------------------------------------------------------------------------
##
proc bibMakeDatabase {} {
set bibTopPat {@([a-zA-Z]+)[\{\(][ ]*}
global PREFS
set bdatout [open "${PREFS}:bibDatabase" w]
# if it fails, but we succeed later, we will have the opportunity
# to rebuild the bibIndex
foreach f [bibListAllBibliographies] {
message "Scanning ${f}…"
openFileQuietly $f
set p 0
while {![catch {search -f 1 -r 1 $bibTopPat $p} epos]} {
set p [lindex $epos 1]
set np [nextLineStart $p]
set entry [string trim [getText $p $np] "\{\( \t\r,"]
if ![catch {search -f 1 -r 1 {title[ \t]*=.*,[ \t]*\r} $np} epos] {
set title [eval getText $epos]
regsub -all "\[\r\t\]+" $title { } title
set title [string range $title [string first "=" $title] end]
set title [string trim $title " =\{\}\","]
puts $bdatout "$entry \{$title\}"
set p [lindex $epos 1]
}
}
killWindow
}
close $bdatout
}